﻿using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using UltimateGameTools.MeshSimplifier;

public class MeshSimplify : MonoBehaviour
{
    [Serializable]
    public enum MeshSimplifyMode
    {
        Split_Mesh,
        Sub_Mesh,
    }

    public MeshSimplifyMode GetMeshSimplifyMode() { return m_meshSimplifyRoot != null ? m_meshSimplifyRoot.m_simplifyMode : m_simplifyMode; }
    public MeshSimplifyMode m_simplifyMode;

    [HideInInspector]
    public Mesh m_originalMesh = null;
    [HideInInspector]
    public int subMeshIdx = -1;

    [HideInInspector]
    public Mesh m_simplifiedMesh = null;

    [HideInInspector]
    public bool m_bEnablePrefabUsage = false;

    [HideInInspector]
    public float m_fVertexAmount = 1.0f;

    [HideInInspector]
    public string m_strAssetPath = null;

    [HideInInspector]
    public MeshSimplify m_meshSimplifyRoot;

    [HideInInspector]
    public List<MeshSimplify> m_listDependentChildren = new List<MeshSimplify>();

    [HideInInspector]
    public bool m_bExpandRelevanceSpheres = true;

    public RelevanceSphere[] m_aRelevanceSpheres = null;

    [SerializeField, HideInInspector]
    protected Simplifier m_meshSimplifier = null;

    [SerializeField, HideInInspector]
    protected bool m_bGenerateIncludeChildren = true;

    [SerializeField, HideInInspector]
    protected bool m_bOverrideRootSettings = false;

    [SerializeField, HideInInspector]
    public bool m_bUseEdgeLength = true, m_bUseCurvature = true, m_bProtectTexture = true, m_bLockBorder = true;

    [SerializeField, HideInInspector]
    protected bool m_bDataDirty = true;

    [SerializeField, HideInInspector]
    protected bool m_bExcludedFromTree = false;

    public IEnumerator ComputeMeshWithVertexCount(GameObject gameObject, Mesh mesh, int vertexCounr, string name, Simplifier.ProgressDelegate progress)
    {
        ConfigureSimplifier();

        if (GetMeshSimplifyMode() == MeshSimplifyMode.Split_Mesh)
            return m_meshSimplifier.ComputeMeshWithVertexCount(gameObject, mesh, vertexCounr, name + " Simplified", progress);
        else
            return m_meshSimplifier.ComputeSubMeshWithVertexCount(gameObject, mesh, vertexCounr, subMeshIdx, name + " Simplified", progress);
    }

#if UNITY_EDITOR

    public void OnDrawGizmos()
    {
        if (m_meshSimplifyRoot != null)
        {
            if (m_meshSimplifyRoot.m_bExpandRelevanceSpheres == false)
            {
                return;
            }
        }
        else
        {
            if (m_bExpandRelevanceSpheres == false)
            {
                return;
            }
        }

        Gizmos.color = Color.red;

        RelevanceSphere[] aRelevanceSpheres = m_meshSimplifyRoot != null ? m_meshSimplifyRoot.m_aRelevanceSpheres : m_aRelevanceSpheres;
        Matrix4x4 l2w = m_meshSimplifyRoot != null ? m_meshSimplifyRoot.transform.localToWorldMatrix : transform.localToWorldMatrix;
        if (aRelevanceSpheres == null)
        {
            return;
        }

        bool bDrawVertices = false;

        for (int i = 0; i < UnityEditor.Selection.gameObjects.Length; i++)
        {
            if (((UnityEditor.Selection.gameObjects[i] == this.gameObject) && m_meshSimplifyRoot == null) || ((m_meshSimplifyRoot != null) && (UnityEditor.Selection.gameObjects[i] == m_meshSimplifyRoot.gameObject)))
            {
                bDrawVertices = true;
            }
        }

        if (bDrawVertices == false)
        {
            return;
        }

        Vector3[] aVerticesWorld = Simplifier.GetWorldVertices(this.gameObject);

        if (aVerticesWorld == null)
        {
            return;
        }

        Matrix4x4[] aSphereMatrices = new Matrix4x4[aRelevanceSpheres.Length];

        for (int nSphere = 0; nSphere < aRelevanceSpheres.Length; nSphere++)
        {
            Matrix4x4 spMat = Matrix4x4.TRS(aRelevanceSpheres[nSphere].m_v3Position, Quaternion.Euler(aRelevanceSpheres[nSphere].m_v3Rotation), aRelevanceSpheres[nSphere].m_v3Scale);
            aSphereMatrices[nSphere] = (l2w * spMat).inverse;

        }

        for (int nVertex = 0; nVertex < aVerticesWorld.Length; nVertex++)
        {
            for (int nSphere = 0; nSphere < aRelevanceSpheres.Length; nSphere++)
            {
                if (aRelevanceSpheres[nSphere].m_bExpanded)
                {
                    Vector3 v3VertexSphereLocal = aSphereMatrices[nSphere].MultiplyPoint(aVerticesWorld[nVertex]);

                    if (v3VertexSphereLocal.magnitude <= 0.5)
                    {
                        Gizmos.DrawCube(aVerticesWorld[nVertex], Vector3.one * UnityEditor.HandleUtility.GetHandleSize(aVerticesWorld[nVertex]) * 0.05f);
                        break;
                    }
                }
            }
        }
    }

#endif

    public static bool HasValidMeshData(GameObject go)
    {
        MeshFilter meshFilter = go.GetComponent<MeshFilter>();

        if (meshFilter != null
            && meshFilter.sharedMesh != null
            && meshFilter.sharedMesh.vertexCount > 4)
        {
            return true;
        }
        else
        {
            SkinnedMeshRenderer skin = go.GetComponent<SkinnedMeshRenderer>();

            if (skin != null
                && skin.sharedMesh != null
                && skin.sharedMesh.vertexCount > 4)
            {
                return true;
            }
        }

        return false;
    }

    public static bool IsRootOrBelongsToTree(MeshSimplify meshSimplify, MeshSimplify root)
    {
        if (meshSimplify == null)
        {
            return false;
        }

        return (meshSimplify.m_bExcludedFromTree == false) && ((meshSimplify.m_meshSimplifyRoot == null) || (meshSimplify.m_meshSimplifyRoot == root) || (meshSimplify == root) || (meshSimplify.m_meshSimplifyRoot == root.m_meshSimplifyRoot));
    }

    public bool IsGenerateIncludeChildrenActive()
    {
        return m_bGenerateIncludeChildren;
    }

    public bool HasDependentChildren()
    {
        return m_listDependentChildren != null && m_listDependentChildren.Count > 0;
    }

    public bool HasDataDirty()
    {
        return m_bDataDirty;
    }

    public bool SetDataDirty(bool bDirty)
    {
        return m_bDataDirty = bDirty;
    }

    public bool HasNonMeshSimplifyGameObjectsInTree()
    {
        return HasNonMeshSimplifyGameObjectsInTreeRecursive(this, this.gameObject);
    }

    private bool HasNonMeshSimplifyGameObjectsInTreeRecursive(MeshSimplify root, GameObject gameObject)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify == null && HasValidMeshData(gameObject))
        {
            return true;
        }

        for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
        {
            if (HasNonMeshSimplifyGameObjectsInTreeRecursive(root, gameObject.transform.GetChild(nChild).gameObject))
            {
                return true;
            }
        }

        return false;
    }

    protected void ConfigureSimplifier()
    {
        m_meshSimplifier.UseEdgeLength = (m_meshSimplifyRoot != null && m_bOverrideRootSettings == false) ? m_meshSimplifyRoot.m_bUseEdgeLength : m_bUseEdgeLength;
        m_meshSimplifier.UseCurvature = (m_meshSimplifyRoot != null && m_bOverrideRootSettings == false) ? m_meshSimplifyRoot.m_bUseCurvature : m_bUseCurvature;
        m_meshSimplifier.ProtectTexture = (m_meshSimplifyRoot != null && m_bOverrideRootSettings == false) ? m_meshSimplifyRoot.m_bProtectTexture : m_bProtectTexture;
        m_meshSimplifier.LockBorder = (m_meshSimplifyRoot != null && m_bOverrideRootSettings == false) ? m_meshSimplifyRoot.m_bLockBorder : m_bLockBorder;
    }

    public Simplifier GetMeshSimplifier()
    {
        return m_meshSimplifier;
    }

    public void ComputeData(bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
    {
        ComputeDataRecursive(this, this.gameObject, bRecurseIntoChildren, progress);
    }

    private static void ComputeDataRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify == null && root.m_bGenerateIncludeChildren)
        {
            if (HasValidMeshData(gameObject))
            {
                meshSimplify = gameObject.AddComponent<MeshSimplify>();
                meshSimplify.m_meshSimplifyRoot = root;
                root.m_listDependentChildren.Add(meshSimplify);
            }
        }

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                meshSimplify.FreeData(false);

                MeshFilter meshFilter = meshSimplify.GetComponent<MeshFilter>();

                if (meshFilter != null && meshFilter.sharedMesh != null)
                {
                    if (meshFilter.sharedMesh.vertexCount > 0)
                    {
                        if (meshSimplify.m_originalMesh == null)
                        {
                            meshSimplify.m_originalMesh = meshFilter.sharedMesh;

                            if (meshSimplify.subMeshIdx == -1)
                                meshSimplify.subMeshIdx = meshFilter.sharedMesh.subMeshCount;
                        }

                        Simplifier[] simplifiers = meshSimplify.GetComponents<Simplifier>();

                        for (int c = 0; c < simplifiers.Length; c++)
                        {
                            if (Application.isEditor && Application.isPlaying == false)
                            {
                                DestroyImmediate(simplifiers[c]);
                            }
                            else
                            {
                                Destroy(simplifiers[c]);
                            }
                        }

                        meshSimplify.m_meshSimplifier = meshSimplify.gameObject.AddComponent<Simplifier>();
                        meshSimplify.m_meshSimplifier.hideFlags = HideFlags.HideInInspector;
                        meshSimplify.ConfigureSimplifier();

                        RelevanceMat[] relevanceMats = null;
                        if (root.m_aRelevanceSpheres.Length > 0)
                        {
                            RelevanceSphere[] aRelevanceSpheres = root.m_aRelevanceSpheres;
                            relevanceMats = new RelevanceMat[aRelevanceSpheres.Length];
                            Matrix4x4 l2w = root.transform.localToWorldMatrix;

                            for (int i = 0; i < relevanceMats.Length; i++)
                            {
                                Matrix4x4 spMat = Matrix4x4.TRS(aRelevanceSpheres[i].m_v3Position, Quaternion.Euler(aRelevanceSpheres[i].m_v3Rotation), aRelevanceSpheres[i].m_v3Scale);
                                relevanceMats[i].m_Mat4x4 = (l2w * spMat).inverse;
                                relevanceMats[i].m_fRelevance = aRelevanceSpheres[i].m_fRelevance;
                            }
                        }

                        IEnumerator enumerator = meshSimplify.m_meshSimplifier.ProgressiveMesh(gameObject, meshSimplify.m_originalMesh, relevanceMats, meshSimplify.name, progress);

                        while (enumerator.MoveNext())
                        {
                            if (Simplifier.Cancelled)
                            {
                                return;
                            }
                        }

                        if (Simplifier.Cancelled)
                        {
                            return;
                        }
                    }
                }
                else
                {
                    SkinnedMeshRenderer skin = meshSimplify.GetComponent<SkinnedMeshRenderer>();

                    if (skin != null)
                    {
                        if (skin.sharedMesh.vertexCount > 0)
                        {
                            if (meshSimplify.m_originalMesh == null)
                            {
                                meshSimplify.m_originalMesh = skin.sharedMesh;
                                if (meshSimplify.subMeshIdx == -1)
                                    meshSimplify.subMeshIdx = skin.sharedMesh.subMeshCount;
                            }

                            Simplifier[] simplifiers = meshSimplify.GetComponents<Simplifier>();

                            for (int c = 0; c < simplifiers.Length; c++)
                            {
                                if (Application.isEditor && Application.isPlaying == false)
                                {
                                    DestroyImmediate(simplifiers[c]);
                                }
                                else
                                {
                                    Destroy(simplifiers[c]);
                                }
                            }

                            meshSimplify.m_meshSimplifier = meshSimplify.gameObject.AddComponent<Simplifier>();
                            meshSimplify.m_meshSimplifier.hideFlags = HideFlags.HideInInspector;
                            meshSimplify.ConfigureSimplifier();

                            RelevanceMat[] relevanceMats = null;
                            if (root.m_aRelevanceSpheres.Length > 0)
                            {
                                RelevanceSphere[] aRelevanceSpheres = root.m_aRelevanceSpheres;
                                relevanceMats = new RelevanceMat[aRelevanceSpheres.Length];
                                Matrix4x4 l2w = root.transform.localToWorldMatrix;

                                for (int i = 0; i < relevanceMats.Length; i++)
                                {
                                    Matrix4x4 spMat = Matrix4x4.TRS(aRelevanceSpheres[i].m_v3Position, Quaternion.Euler(aRelevanceSpheres[i].m_v3Rotation), aRelevanceSpheres[i].m_v3Scale);
                                    relevanceMats[i].m_Mat4x4 = (l2w * spMat).inverse;
                                    relevanceMats[i].m_fRelevance = aRelevanceSpheres[i].m_fRelevance;
                                }
                            }

                            IEnumerator enumerator = meshSimplify.m_meshSimplifier.ProgressiveMesh(gameObject, meshSimplify.m_originalMesh, relevanceMats, meshSimplify.name, progress);

                            while (enumerator.MoveNext())
                            {
                                if (Simplifier.Cancelled)
                                {
                                    return;
                                }
                            }

                            if (Simplifier.Cancelled)
                            {
                                return;
                            }
                        }
                    }
                }

                meshSimplify.m_bDataDirty = false;
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                ComputeDataRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren, progress);

                if (Simplifier.Cancelled)
                {
                    return;
                }
            }
        }
    }

    public bool HasData()
    {
        return (m_meshSimplifier != null && m_simplifiedMesh != null) || (m_listDependentChildren != null && m_listDependentChildren.Count != 0);
    }

    public bool HasSimplifiedMesh()
    {
        return m_simplifiedMesh != null && m_simplifiedMesh.vertexCount > 0;
    }

    public void ComputeMesh(bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
    {
        ComputeMeshRecursive(this, this.gameObject, bRecurseIntoChildren, progress);
    }

    private static void ComputeMeshRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                if (meshSimplify.m_meshSimplifier != null)
                {
                    if (meshSimplify.m_simplifiedMesh)
                    {
                        meshSimplify.m_simplifiedMesh.Clear();
                    }

                    float fAmount = meshSimplify.m_fVertexAmount;

                    if (meshSimplify.m_bOverrideRootSettings == false && meshSimplify.m_meshSimplifyRoot != null)
                    {
                        fAmount = meshSimplify.m_meshSimplifyRoot.m_fVertexAmount;
                    }

                    if (meshSimplify.m_simplifiedMesh == null)
                    {
                        meshSimplify.m_simplifiedMesh = CreateNewEmptyMesh(meshSimplify);
                    }
#if false
                    meshSimplify.ConfigureSimplifier();
                    IEnumerator enumerator = meshSimplify.m_meshSimplifier.ComputeMeshWithVertexCount(gameObject, meshSimplify.m_simplifiedMesh, Mathf.RoundToInt(fAmount * meshSimplify.m_meshSimplifier.GetOriginalMeshUniqueVertexCount()), meshSimplify.name + " Simplified", progress);
#else
                    IEnumerator enumerator = meshSimplify.ComputeMeshWithVertexCount(gameObject, meshSimplify.m_simplifiedMesh, Mathf.RoundToInt(fAmount * meshSimplify.m_meshSimplifier.GetOriginalMeshUniqueVertexCount()), meshSimplify.name + " Simplified", progress);
#endif
                    while (enumerator.MoveNext())
                    {
                        if (Simplifier.Cancelled)
                        {
                            return;
                        }
                    }

                    if (Simplifier.Cancelled)
                    {
                        return;
                    }
                }
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                ComputeMeshRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren, progress);

                if (Simplifier.Cancelled)
                {
                    return;
                }
            }
        }
    }

    public void AssignSimplifiedMesh(bool bRecurseIntoChildren)
    {
        AssignSimplifiedMeshRecursive(this, this.gameObject, bRecurseIntoChildren);
    }

    private static void AssignSimplifiedMeshRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                if (meshSimplify.m_simplifiedMesh != null)
                {
                    MeshFilter meshFilter = meshSimplify.GetComponent<MeshFilter>();

                    if (meshFilter != null)
                    {
                        meshFilter.sharedMesh = meshSimplify.m_simplifiedMesh;
                    }
                    else
                    {
                        SkinnedMeshRenderer skin = meshSimplify.GetComponent<SkinnedMeshRenderer>();

                        if (skin != null)
                        {
                            skin.sharedMesh = meshSimplify.m_simplifiedMesh;
                        }
                    }
                }
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                AssignSimplifiedMeshRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren);
            }
        }
    }

    public void ReplaceOriginalMesh(bool bDeleteData, bool bRecurseIntoChildren)
    {
        ReplaceOriginalMeshRecursive(this, this.gameObject, bDeleteData, bRecurseIntoChildren);
    }

    private static void ReplaceOriginalMeshRecursive(MeshSimplify root, GameObject gameObject, bool bDeleteData, bool bRecurseIntoChildren)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                if (meshSimplify.m_originalMesh != null)
                {
                    if (meshSimplify.m_simplifiedMesh != null)
                    {

                        Mesh meshIn = meshSimplify.m_simplifiedMesh;
                        Mesh meshOut = meshSimplify.m_originalMesh;

#if UNITY_EDITOR
                        string parentFolder = Application.dataPath.Replace("Assets", "");
#endif
                        meshOut.triangles = new int[0];
                        meshOut.subMeshCount = 0;
#if false
                        meshOut.vertices = meshIn.vertices;
                        meshOut.normals = meshIn.normals;
                        meshOut.tangents = meshIn.tangents;
                        meshOut.uv = meshIn.uv;
                        meshOut.uv2 = meshIn.uv2;
                        meshOut.colors32 = meshIn.colors32;
                        meshOut.boneWeights = meshIn.boneWeights;
                        meshOut.bindposes = meshIn.bindposes;
#endif
                        meshOut.triangles = meshIn.triangles;
                        meshOut.subMeshCount = meshIn.subMeshCount;

                        for (int nSubMesh = 0; nSubMesh < meshIn.subMeshCount; nSubMesh++)
                        {
                            meshOut.SetTriangles(meshIn.GetTriangles(nSubMesh), nSubMesh);
                        }

#if UNITY_EDITOR
                        UnityEditor.EditorUtility.SetDirty(meshOut);
#endif
                    }

                    MeshFilter meshFilter = meshSimplify.GetComponent<MeshFilter>();

                    if (meshFilter != null)
                    {
                        meshFilter.sharedMesh = meshSimplify.m_originalMesh;
                    }
                    else
                    {
                        SkinnedMeshRenderer skin = meshSimplify.GetComponent<SkinnedMeshRenderer>();

                        if (skin != null)
                        {
                            skin.sharedMesh = meshSimplify.m_originalMesh;
                        }
                    }
                }

                if (bDeleteData)
                {
                    meshSimplify.FreeData(false);
                    meshSimplify.m_listDependentChildren.Clear();
                }
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                ReplaceOriginalMeshRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bDeleteData, bRecurseIntoChildren);
            }
        }
    }

    public void RestoreOriginalMesh(bool bDeleteData, bool bRecurseIntoChildren)
    {
        RestoreOriginalMeshRecursive(this, this.gameObject, bDeleteData, bRecurseIntoChildren);
    }

    private static void RestoreOriginalMeshRecursive(MeshSimplify root, GameObject gameObject, bool bDeleteData, bool bRecurseIntoChildren)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                if (meshSimplify.m_originalMesh != null)
                {
                    MeshFilter meshFilter = meshSimplify.GetComponent<MeshFilter>();

                    if (meshFilter != null)
                    {
                        meshFilter.sharedMesh = meshSimplify.m_originalMesh;
                    }
                    else
                    {
                        SkinnedMeshRenderer skin = meshSimplify.GetComponent<SkinnedMeshRenderer>();

                        if (skin != null)
                        {
                            skin.sharedMesh = meshSimplify.m_originalMesh;
                        }
                    }
                }

                if (bDeleteData)
                {
                    meshSimplify.FreeData(false);
                    meshSimplify.m_listDependentChildren.Clear();
                }
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                RestoreOriginalMeshRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bDeleteData, bRecurseIntoChildren);
            }
        }
    }

    public bool HasOriginalMeshActive(bool bRecurseIntoChildren)
    {
        return HasOriginalMeshActiveRecursive(this, this.gameObject, bRecurseIntoChildren);
    }

    private static bool HasOriginalMeshActiveRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        bool bHasOriginalMeshActive = false;

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                if (meshSimplify.m_originalMesh != null)
                {
                    MeshFilter meshFilter = meshSimplify.GetComponent<MeshFilter>();

                    if (meshFilter != null)
                    {
                        if (meshFilter.sharedMesh == meshSimplify.m_originalMesh)
                        {
                            bHasOriginalMeshActive = true;
                        }
                    }
                    else
                    {
                        SkinnedMeshRenderer skin = meshSimplify.GetComponent<SkinnedMeshRenderer>();

                        if (skin != null)
                        {
                            if (skin.sharedMesh == meshSimplify.m_originalMesh)
                            {
                                bHasOriginalMeshActive = true;
                            }
                        }
                    }
                }
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                bHasOriginalMeshActive = bHasOriginalMeshActive || HasOriginalMeshActiveRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren);
            }
        }

        return bHasOriginalMeshActive;
    }

    public bool HasVertexData(bool bRecurseIntoChildren)
    {
        return HasVertexDataRecursive(this, this.gameObject, bRecurseIntoChildren);
    }

    private static bool HasVertexDataRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                if (meshSimplify.m_simplifiedMesh && meshSimplify.m_simplifiedMesh.vertexCount > 0)
                {
                    return true;
                }
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                if (HasVertexDataRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren))
                {
                    return true;
                }
            }
        }

        return false;
    }

    public int GetOriginalVertexCount(bool bRecurseIntoChildren)
    {
        int nVertexCount = 0;
        GetOriginalVertexCountRecursive(this, this.gameObject, ref nVertexCount, bRecurseIntoChildren);
        return nVertexCount;
    }

    private static void GetOriginalVertexCountRecursive(MeshSimplify root, GameObject gameObject, ref int nVertexCount, bool bRecurseIntoChildren)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                if (meshSimplify.m_originalMesh != null)
                {
                    nVertexCount += meshSimplify.m_originalMesh.vertexCount;
                }
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                GetOriginalVertexCountRecursive(root, gameObject.transform.GetChild(nChild).gameObject, ref nVertexCount, bRecurseIntoChildren);
            }
        }
    }

    public int GetSimplifiedVertexCount(bool bRecurseIntoChildren)
    {
        int nVertexCount = 0;
        GetSimplifiedVertexCountRecursive(this, this.gameObject, ref nVertexCount, bRecurseIntoChildren);
        return nVertexCount;
    }

    private static void GetSimplifiedVertexCountRecursive(MeshSimplify root, GameObject gameObject, ref int nVertexCount, bool bRecurseIntoChildren)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                if (meshSimplify.m_simplifiedMesh != null)
                {
                    nVertexCount += meshSimplify.m_simplifiedMesh.vertexCount;
                }
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                GetSimplifiedVertexCountRecursive(root, gameObject.transform.GetChild(nChild).gameObject, ref nVertexCount, bRecurseIntoChildren);
            }
        }
    }

    public void RemoveFromTree()
    {
        if (m_meshSimplifyRoot != null)
        {
            m_meshSimplifyRoot.m_listDependentChildren.Remove(this);
        }

        RestoreOriginalMesh(true, false);

        m_bExcludedFromTree = true;
    }

    public void FreeData(bool bRecurseIntoChildren)
    {
        FreeDataRecursive(this, this.gameObject, bRecurseIntoChildren);
    }

    private static void FreeDataRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                if (meshSimplify.m_simplifiedMesh)
                {
                    meshSimplify.m_simplifiedMesh.Clear();
                }

                Simplifier[] simplifiers = gameObject.GetComponents<Simplifier>();

                for (int c = 0; c < simplifiers.Length; c++)
                {
                    if (Application.isEditor && Application.isPlaying == false)
                    {
                        DestroyImmediate(simplifiers[c]);
                    }
                    else
                    {
                        Destroy(simplifiers[c]);
                    }
                }

                meshSimplify.m_bDataDirty = true;
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                FreeDataRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren);
            }
        }
    }

    private static Mesh CreateNewEmptyMesh(MeshSimplify meshSimplify)
    {
        if (meshSimplify.m_originalMesh == null)
        {
            return new Mesh();
        }

        Mesh meshOut = Mesh.Instantiate(meshSimplify.m_originalMesh);
        meshOut.Clear();
        return meshOut;
    }

#if UNITY_EDITOR

    public void DisablePrefabUsage(bool bRecurseIntoChildren)
    {
        DisablePrefabUsageRecursive(this, this.gameObject, bRecurseIntoChildren);
    }

    private static void DisablePrefabUsageRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren)
    {
        MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();

        if (meshSimplify != null)
        {
            if (IsRootOrBelongsToTree(meshSimplify, root))
            {
                if (meshSimplify.m_simplifiedMesh)
                {
                    if (UnityEditor.AssetDatabase.IsMainAsset(meshSimplify.m_simplifiedMesh) || UnityEditor.AssetDatabase.IsSubAsset(meshSimplify.m_simplifiedMesh))
                    {
                        Mesh newMesh = Instantiate(meshSimplify.m_simplifiedMesh) as Mesh;
                        meshSimplify.m_simplifiedMesh = newMesh;
                    }
                }

                meshSimplify.m_strAssetPath = null;
            }
        }

        if (bRecurseIntoChildren)
        {
            for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
            {
                DisablePrefabUsageRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren);
            }
        }
    }

#endif
}
